package com.heima.article.job;

import com.alibaba.fastjson.JSON;
import com.heima.article.service.HotArticleService;
import com.heima.common.constants.article.HotArticleConstants;
import com.heima.model.mess.app.AggBehaviorDTO;
import com.heima.model.mess.app.NewBehaviorDTO;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author mrchen
 * @date 2022/7/16 14:45
 */
@Component
@Slf4j
public class UpdateHotArticleJob {

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    HotArticleService hotArticleService;

    @XxlJob("updateHotArticleJob")
    public ReturnT updateHotArticleJob(String params){
        log.info("热文章分值更新 调度任务开始执行....");
        // 1. 读取最近10s,redis所缓存得最新文章行为数据集合
        List<NewBehaviorDTO> newBehaviorList = getRedisBehaviorList();
        if (CollectionUtils.isEmpty(newBehaviorList)) {
            log.info("太冷清了，最近10s 我们得文章没有产生任何行为信息~~~");
            return ReturnT.SUCCESS;
        }
        // 2. 对数据做聚合统计运算
        List<AggBehaviorDTO> aggBehaviorLis = getAggBehaviorList(newBehaviorList);
        if (CollectionUtils.isEmpty(aggBehaviorLis)) {
            log.info("太冷清了，最近10s 我们得文章没有产生任何行为信息~~~");
            return ReturnT.SUCCESS;
        }
        // 3. 根据结果修改文章热度
        aggBehaviorLis.forEach(aggBehaviorDTO -> {
            hotArticleService.updateApArticle(aggBehaviorDTO);
        });
        log.info("热文章分值更新 调度任务完成....");
        return ReturnT.SUCCESS;
    }

    private List<AggBehaviorDTO> getAggBehaviorList(List<NewBehaviorDTO> newBehaviorList) {
        // 1. 按照文章id对行为数据进行分组  Map<文章id,List<NewBehaviorDTO>>
        Map<Long, List<NewBehaviorDTO>> map = newBehaviorList.stream()
                .collect(Collectors.groupingBy(NewBehaviorDTO::getArticleId));
        // 2. 循环遍历行为分组，将每一个分组数据 ==> 封装成 AggBehaviorDTO对象
        //  [{type:VIEWS,add:1,articleId:123}{type:VIEWS,add:1,articleId:123}{type:VIEWS,add:1,articleId:123}]
        //  {articleId: 123, views: ,likes: comment: ,collect:}
        List<AggBehaviorDTO> aggBehaviorList = new ArrayList<>();
        map.forEach((articleId,behaviorList)->{
            Optional<AggBehaviorDTO> reduceResult = behaviorList.stream()
                    .map(behavior -> {
                        AggBehaviorDTO aggBehaviorDTO = new AggBehaviorDTO();
                        aggBehaviorDTO.setArticleId(articleId);
                        switch (behavior.getType()) {
                            case VIEWS:
                                aggBehaviorDTO.setView(behavior.getAdd());
                                break;
                            case LIKES:
                                aggBehaviorDTO.setLike(behavior.getAdd());
                                break;
                            case COMMENT:
                                aggBehaviorDTO.setComment(behavior.getAdd());
                                break;
                            case COLLECTION:
                                aggBehaviorDTO.setCollect(behavior.getAdd());
                                break;
                        }
                        return aggBehaviorDTO;
                    }).reduce((a1, a2) -> {
                        a1.setView(a1.getView() + a2.getView());
                        a1.setLike(a1.getLike() + a2.getLike());
                        a1.setComment(a1.getComment() + a2.getComment());
                        a1.setCollect(a1.getCollect() + a2.getCollect());
                        return a1;
                    });
            aggBehaviorList.add(reduceResult.get());
        });
        // 3. 将AggBehavior对象收集到一个集合中，返回结果
        return aggBehaviorList;
    }

    /**
     * 获取redis中产生得最新行为数据
     *    llen   lrange   ltrim
     * @return
     */
    private List<NewBehaviorDTO> getRedisBehaviorList() {
        // 1. 创建脚本对象
        DefaultRedisScript<List> script = new DefaultRedisScript<>();
        // 2. 设置返回类型  脚本地址
        script.setResultType(List.class);
        script.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis.lua")));
        // 3. 执行脚本得到返回结果 List<String>
        List<String> result = redisTemplate.execute(script, Arrays.asList(
                HotArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST
        ));
        // 4. 将得到得集合  转为 List<NewBehaviorDTO>
        return result.stream()
                .map(jsonStr -> JSON.parseObject(jsonStr, NewBehaviorDTO.class))
                .collect(Collectors.toList());
    }

}
